package com.ruoyi.shop.protocol.websocket.server;

import com.alibaba.fastjson.JSON;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.shop.nosql.mongo.domain.Message;
import com.ruoyi.shop.nosql.mongo.service.IMessageService;
import com.ruoyi.shop.protocol.websocket.domain.MessageList;
import com.ruoyi.shop.protocol.websocket.domain.User;
import com.ruoyi.shop.protocol.websocket.domain.UserList;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static com.ruoyi.shop.common.constant.Constants.USER_INFO;

/**
 * WebSocket 服务
 */
@Component
@Slf4j
@ServerEndpoint("/socket/{userId}")
public class WebSocketServer {
    private final RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
    private final ISysUserService sysUserService = SpringUtils.getBean(ISysUserService.class);
    private final IMessageService messageService = SpringUtils.getBean(IMessageService.class);

    /**
     * 存储对象 map
     */
    public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();

    /***
     * WebSocket 建立连接事件
     * 1.把登录的用户存到 sessionMap 中
     * 2.发送给所有人当前登录人员信息
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {

        // 搜索名称是否存在
        boolean isExist = sessionMap.containsKey(userId);
        if (!isExist) {
            System.out.println(userId + "加入了聊天室");
            sessionMap.put(userId, session);
            // 将用户信息存入Redis缓存
            saveUserInfoToRedis(userId);
            // 发送当前登录人员信息给所有在线用户
            sendAllMessage(setUserList());
            showUserList();
        }
    }

    /**
     * 将用户信息存入Redis缓存
     * <p>
     * 1.获取当前登录用户信息
     * 2.创建User对象并设置用户信息
     * 3.将用户信息存入Redis缓存
     * 4.缓存键为 USER_INFO + 用户ID
     *
     * @param userId
     * @return
     */
    public User saveUserInfoToRedis(String userId) {
        // 获取当前登录用户信息
        System.out.println("userId:" + userId);
        SysUser sysUser = sysUserService.selectUserById(Long.valueOf(userId));
        // 创建User对象并设置用户信息
        User user = new User(userId, sysUser.getNickName(), sysUser.getAvatar());
        // 将用户信息存入Redis缓存
        // 缓存键为 USER_INFO + 用户ID
        redisCache.setCacheObject(USER_INFO + userId, user);
        return user;
    }

    /**
     * WebSocket 关闭连接事件
     * 1.把登出的用户从 sessionMap 中剃除
     * 2.发送给所有人当前登录人员信息
     */
    @OnClose
    public void onClose(@PathParam("userId") String userId) {
        if (userId != null) {
            System.out.println(userId + "退出了聊天室");
            sessionMap.remove(userId);
            sendAllMessage(setUserList());
            showUserList();
        }
    }

    /**
     * WebSocket 接受信息事件
     * 接收处理客户端发来的数据
     * @param message 信息
     */
    @OnMessage
    public void onMessage(String message) {
        System.out.println("收到消息：" + message);
        Message msg = JSON.parseObject(message, Message.class);
        switch (msg.getType()) {
            case 0://系统消息：获取消息记录
                MessageList messageList = new MessageList();
                List<Message> messages = messageService.queryList(msg);
                messageList.setMessageList(messages);
                //返回消息记录给用户
                sendMessage(JSON.toJSONString(messageList), msg.getSenderId());
                break;
            case 1://向另一个用户发送消息
                //插入消息记录
                messageService.addMessage(msg);
                //发送给指定用户
                sendMessage(message, msg.getReceiverId());
                sendMessage(message, msg.getSenderId());
                break;
            case 2: //发送给所有用户
                sendAllMessage(message);
                break;
            default:
                break;
        }
    }

    /**
     * WebSocket 错误事件
     * @param session 用户 Session
     * @param error 错误信息
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 显示在线用户
     */
    private void showUserList() {
        System.out.println("------------------------------------------");
        System.out.println("当前在线用户");
        System.out.println("------------------------------------------");
        for (String userId : sessionMap.keySet()) {
            System.out.println(userId);
        }
        System.out.println("------------------------------------------");
        System.out.println();
    }

    /**
     * 设置接收消息的用户列表
     * @return 用户列表
     */
    private String setUserList(){
        ArrayList<String> list = new ArrayList<>(sessionMap.keySet());
        UserList userList = new UserList();
        List<User> users = new ArrayList<>();
        for (String userId : list) {
            //从redis中获取用户信息
            User user = redisCache.getCacheObject(USER_INFO + userId);
            if (user == null) {
                user = saveUserInfoToRedis(userId);
            }
            users.add(user);
            log.info("用户信息：{}", user);
        }
        userList.setUserList(users);
        return JSON.toJSONString(userList);
    }

    /**
     * 发送消息到所有用户
     * @param message 消息
     */
    public static void sendAllMessage(String message) {
        try {
            for (Session session : sessionMap.values()) {
                session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送消息到单个用户
     *
     * @param message 消息
     * @param userId  用户ID
     */
    public static void sendMessage(String message, String userId) {
        try {
            sessionMap.get(userId).getBasicRemote().sendText(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送消息到多个用户
     *
     * @param message 消息
     * @param userIds 用户ID列表
     */
    public static void sendMessage(String message, List<String> userIds) {
        try {
            for (String userId : userIds) {
                sessionMap.get(userId).getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
